package com.coincalf.exchange.task;

import com.coincalf.exchange.entity.EntrustOrder;
import com.coincalf.exchange.entity.Market;
import com.coincalf.exchange.service.EntrustOrderService;
import com.coincalf.exchange.service.LockService;
import com.coincalf.exchange.service.impl.MatchService;
import com.coincalf.framework.constants.Constant;
import com.coincalf.framework.enums.OrderType;
import com.coincalf.framework.exception.TradeMatchException;
import com.coincalf.framework.exception.VolumeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * @Description: 撮合任务
 * @Author: Chen Long
 * @Date: Created in 2018/5/13 下午2:06
 * @Modified by: Chen Long
 */
@Slf4j
@Component
public class MatchTask implements Constant {

    @Autowired
    private MatchService matchService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private EntrustOrderService entrustOrderService;
    @Autowired
    private LockService lockService;
    @Autowired
    private AccountTask accountTask;

    /**
     * 币币交易撮合
     *
     * @param market
     */
    @Async("tradeMatchTaskAsyncPool")
    public void tradeMatch(Market market, String orderId) {
        long start = System.currentTimeMillis();
        boolean isLocked = false;
        String matchOrderId = null;
        // 币币交易撮合队列Redis存储Key
        String redisKey = new StringBuffer(REDIS_KEY_TRADE_MATCH).append(market.getSymbol()).toString();
        try {
            log.info("币币交易撮合=> 开始撮合委托订单，orderId：{}", orderId);
            EntrustOrder entrustOrder = entrustOrderService.selectById(Long.parseLong(orderId));
            if (entrustOrder == null) {
                // 订单不存在
                log.error("币币交易撮合=> 订单不存在，orderId：{}", orderId);
                return;
            }
            if (entrustOrder.getStatus() != 0) {
                // 订单状态错误
                log.error("币币交易撮合=> 订单状态错误，orderId：{}，status：{}", orderId, entrustOrder.getStatus());
                return;
            }
            // 未成交数量 = 委托总数量 - 已撮合处理数量
            BigDecimal needDeal = entrustOrder.getVolume().subtract(entrustOrder.getDeal());
            if (needDeal.compareTo(BigDecimal.ZERO) < 1) {
                // 委托数量错误
                log.error("币币交易撮合=> 委托数量错误，orderId：{}，待成交量：{}", orderId, needDeal);
                entrustOrderService.modifyTradeEntrustOrderToDeal(entrustOrder.getId());
                return;
            }
            // 查询匹配的委托单类型
            OrderType matchOrderType = OrderType.getByCode(entrustOrder.getType()) == OrderType.BUY ? OrderType.SELL : OrderType.BUY;
            // 查询匹配的委托单
            List<EntrustOrder> matchOrderList = entrustOrderService.queryMatchEntrustOrder(market.getId(), matchOrderType, entrustOrder.getPrice());
            if (CollectionUtils.isEmpty(matchOrderList)) {
                log.info("币币交易撮合=> 找不到可与之撮合的委托单");
                return;
            }
            EntrustOrder matchOrder = null;
            for (EntrustOrder order : matchOrderList) {
                matchOrderId = String.valueOf(order.getId());
                isLocked = lockService.getLock(REDIS_KEY_TRADE_ORDER_MATCH_LOCK, matchOrderId, false);
                if (isLocked) {
                    matchOrder = order;
                    break;
                }
            }
            if (matchOrder == null) {
                redisTemplate.opsForList().leftPush(redisKey, orderId);
                return;
            }
            // 未成交数量
            needDeal = matchOrder.getVolume().subtract(matchOrder.getDeal());
            if (needDeal.compareTo(BigDecimal.ZERO) < 1) {
                // 委托数量错误
                log.error("币币交易撮合=> 委托数量错误，orderId：{}，待成交量：{}", matchOrder.getId(), needDeal);
                entrustOrderService.modifyTradeEntrustOrderToDeal(matchOrder.getId());
                return;
            }
            // 币币交易撮合
            Map<String, Object> matchResult = matchService.tradeMatch(entrustOrder, matchOrder, market, OrderType.getByCode(entrustOrder.getType()));
            boolean isPartMatch = (boolean) matchResult.get(MATCH_RESULT_PART_MATCH);
            // 撮合完成修改资金账户
            accountTask.tradeMatchModifyAccount(matchResult);
            if (isPartMatch) {
                // 部分撮合则重新加入队列继续撮合
                redisTemplate.opsForList().leftPush(redisKey, orderId);
            }
        } catch (VolumeException e) {
            log.error(e.getMessage());
            // 成交量异常
            redisTemplate.opsForList().leftPush(redisKey, orderId);
        } catch (TradeMatchException e) {
            log.error(e.getMessage());
            throw e;
        } finally {
            if (isLocked && !StringUtils.isEmpty(matchOrderId)) {
                lockService.unlock(REDIS_KEY_TRADE_ORDER_MATCH_LOCK, matchOrderId);
            }
        }
        long end = System.currentTimeMillis();
        log.info("币币交易撮合成功，orderId:{}，耗时：{} 毫秒", orderId, (end - start));
    }

}
